#include <stdlib.h>

#include "lib.h"

#include "slotguts.h"
#include "csguts.h"
#include "slotspin.h"
#include "getrand.h"
#include "sltsql.h"
#include "slotconn.h"
#include "opts.h"
#include "cardmgr.h"
#include "shue.h"
#include "cscrdhand.h"
#include "log.h"

static int32 g_BigBonusPayouts[] =
{
	//The 128 big bonus payouts, form the big bonus sheet in the worksheet
	9	,
	7	,
	7	,
	6	,
	6	,
	6	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	3	,
	3	,
	3	,
	3	,
	3	,
	3	,
	3	,
	3	,
	3	,
	3	,
	3	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	2	,
	1	,
	1	,
	1	,
	1	,
	1	,
	1	
};


static int32 g_SmallBonusPayouts[] = 
{
	//The 128 small bonus payouts, from the small bonus sheet in the worksheet
	11	,
	9	,
	9	,
	8	,
	8	,
	8	,
	7	,
	7	,
	7	,
	7	,
	7	,
	7	,
	7	,
	7	,
	7	,
	7	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	6	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	5	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	4	,
	3	,
	3	,
	3	,
	3	,
	2	,
	2	

};

enum WheelContents {
    BLANK,
	OYSTER,
    RAINBOWFISH,
    OCTOPUS,
    SANDDOLLAR,
    SEAHORSE,
    CARDSHARK,
	STARFISH,
	TREASUREOPEN,
	TREASUREHALF,
	TREASURE
};


enum SpinResult
{
	//The order here looks odd, 
	//but it matches the paytable,
	//and is ordered by payout size
	EVT_LOSER = 0,
	EVT_CHESTS,
	EVT_CARDSHARK,
	EVT_2SANDDOLLAR,
	EVT_3OYSTER,
	EVT_2SEAHORSE,
	EVT_3RAINBOWFISH,
	EVT_4OYSTER,
	EVT_2STARFISH,
	EVT_3OCTOPUS,
	EVT_4RAINBOWFISH,
	EVT_3SANDDOLLAR,
	EVT_5OYSTER,
	EVT_4OCTOPUS,
	EVT_3SEAHORSE,
	EVT_5RAINBOWFISH,
	EVT_3STARFISH,
	EVT_4SANDDOLLAR,
	EVT_5OCTOPUS,
	EVT_4SEAHORSE,
	EVT_5SANDDOLLAR,
	EVT_5SEAHORSE,
	EVT_4STARFISH,
	EVT_5STARFISH,

	NUMEVENTS
};

SlotSpin *
CardSharkSlotGuts::makeJackpot()
{
    uint8 *stops = new uint8[getReelCount()];
    SlotSpin *spin = new SlotSpin();

    spin->setBet(0);
    spin->setBonusGameType(0);


    /**
     * Hard set the reels to jackpots.  The assumption is
     * that the jackpot icon is the second to last icon on
     * the strip (the last being a blank).
     */
    for (uint8 i = 0; i < getReelCount(); i++)
    {
        stops[i] = getIcon(i, getIconCount(i) - 2)->position;
    }

    spin->setStops(getReelCount(), stops);

    classifyResult(spin);

    m_spins.push_back(spin);

    delete [] stops;

    return spin;
}




bool8 
CardSharkSlotGuts::isValidGuts()
{
    return (getGutsId() >= 16109 && getGutsId() < 16200);
}

int32
CardSharkSlotGuts::getMaxAggregate()
{
    return 60750;
}


uint8
CardSharkSlotGuts::classifyStops(uint8* stops,uint8 bet, uint8 line)
{
	uint8 result = EVT_LOSER;


	uint8 whichIcon = BLANK;
	uint8 testIcon = BLANK;
	bool8 streak = true; //Every line has a streak of at least one

	//Check the first icon (the rest have to match it)
	whichIcon = getIcon(0, stops[0])->iconid;
	testIcon = whichIcon;

	//Now, go through the rest, until the streak is over
	for ( uint8 i = 1; (i < getReelCount())&&(streak); i++ )
    {
		testIcon = getIcon(i, stops[i])->iconid;
		if(testIcon != whichIcon)
		{
			streak = false;
		}
	}
	
	//i is now the number of icons in a row (+1 if streak is false),
	//so pay it out
	if(!streak)
	{
		i-=1;
	}
	switch(i)
	{
		case 2:
			switch(whichIcon)
			{
				case OYSTER:
					break;
				case RAINBOWFISH:
					break;
				case OCTOPUS:
					break;
				case SANDDOLLAR:
					result = EVT_2SANDDOLLAR;
					break;
				case SEAHORSE:
					result = EVT_2SEAHORSE;
					break;
				case STARFISH:
					result = EVT_2STARFISH;
					break;
			}
			break;		
		case 3:
			switch(whichIcon)
			{
				case OYSTER:
					result = EVT_3OYSTER;
					break;
				case RAINBOWFISH:
					result = EVT_3RAINBOWFISH;
					break;
				case OCTOPUS:
					result = EVT_3OCTOPUS;
					break;
				case SANDDOLLAR:
					result = EVT_3SANDDOLLAR;
					break;
				case SEAHORSE:
					result = EVT_3SEAHORSE;
					break;
				case STARFISH:
					result = EVT_3STARFISH;
					break;
			}
			break;
		case 4:
			switch(whichIcon)
			{
				case OYSTER:
					result = EVT_4OYSTER;
					break;
				case RAINBOWFISH:
					result = EVT_4RAINBOWFISH;
					break;
				case OCTOPUS:
					result = EVT_4OCTOPUS;
					break;
				case SANDDOLLAR:
					result = EVT_4SANDDOLLAR;
					break;
				case SEAHORSE:
					result = EVT_4SEAHORSE;
					break;
				case STARFISH:
					result = EVT_4STARFISH;
					break;
			}
			break;
		case 5:
			switch(whichIcon)
			{
				case OYSTER:
					result = EVT_5OYSTER;
					break;
				case RAINBOWFISH:
					result = EVT_5RAINBOWFISH;
					break;
				case OCTOPUS:
					result = EVT_5OCTOPUS;
					break;
				case SANDDOLLAR:
					result = EVT_5SANDDOLLAR;
					break;
				case SEAHORSE:
					result = EVT_5SEAHORSE;
					break;
				case STARFISH:
					result = EVT_5STARFISH;
					break;
			}
			break;
	}

	return result;
}

void
CardSharkSlotGuts::classifyScatters(SlotSpin *spin)
{

	//Manually traverse through the 15 positions, looking for foodfight or ice cream icons.
	//Three of either is a winner
	uint8 numChestIcons = 0;
	uint8 numSharkIcons = 0;
	uint8 ChestsY[3];
	uint8 stop = 0;
	uint8 whichItem = BLANK;
	int32 payout = 0;
	for(int reel = 0; reel<5; reel++)
	{
		for(int row = 0; row < 3; row++)
		{
			stop = relativeWheelPosition(reel, spin->getStops()[reel], row);
			whichItem = getIcon(reel, stop)->iconid;

			//Chests
			if(whichItem >= TREASUREOPEN && whichItem <= TREASURE)
			{
				ChestsY[numChestIcons] = row; //We send the client where the pieces are
				numChestIcons++;
			}
			//Card Shark
			else if (whichItem == CARDSHARK)
			{
				numSharkIcons++;
			}
		} //row
	} //reel

//BEGIN FUDGE
/*
	//FUDGE IT FOR NOW
	numChestIcons = 0;
	ChestsY[0] = 1;
	ChestsY[1] = 1;
	ChestsY[2] = 2;
	numSharkIcons = 3;
*/
//END FUDGE


	//Set the winners
	if(numChestIcons >= 3)
	{
		//If it's chests, go ahead and play it (we don't need no stinkin' bonus game)
		m_subGame->update(SGIDX_BONUSGAME,BGCHESTS);
		m_subGame->update(SGIDX_CHESTONEY,ChestsY[0]);
		m_subGame->update(SGIDX_CHESTTWOY,ChestsY[1]);
		m_subGame->update(SGIDX_CHESTTHREEY,ChestsY[2]);
		payout = getRandomFromArray(g_SmallBonusPayouts, 128)*(spin->getTotalBet());
		m_subGame->update(SGIDX_CHESTVALUE,payout);
		readytofinish = false;

	}
	else if(numSharkIcons >=3)
	{
		//If card shark, we go ahead and initialize everything
		totalBet = spin->getTotalBet(); //It'll get lost by the time the bonus game play comes back
		m_subGame->update(SGIDX_BONUSGAME,BGCARDSHARK);
		initializeSharkAttack();
		readytofinish = false;
	}
	else
	{
		//You can't win 'em all
		m_subGame->update(SGIDX_BONUSGAME,BGNONE);
		readytofinish = true;
	}
}

void
CardSharkSlotGuts::initializeSharkAttack()
{
	CardMgr* cards;
	Shue* shue;
	uint32 deckSeed;
	uint8 cardsInHand = 4;

	//Shuffle the deck
	cards = new CardMgr();

	ShueGameInfo sgi;
	sgi.gameId = GAMEID;
	sgi.machId = getMachId();

	shue = new Shue(cards,1,sgi); //1 deck
	hand = new CardSharkHand(cards);
	
	// CSN-11524: When getting random value, make sure data type in the database does not overflow
	do
	{
		deckSeed = getrand();
	} while (deckSeed > MAX_DECK_DATATYPE_SIZE);
	
	shue->shuffle(deckSeed);
	m_subGame->setServerOnly(SGIDX_CSSERVDECKSEED,true); //server only	
	m_subGame->update(SGIDX_CSSERVDECKSEED,deckSeed);

	//Deal four cards
	for(uint8 i=0; i<cardsInHand; i++)
	{
		hand->add(shue->getCard());
	}

	//Put them into the subgamedata
	m_subGame->update(SGIDX_CSCARDONE,hand->getCardAt(0)); 	

	m_subGame->setServerOnly(SGIDX_CSSERVCARDTWO,true); //server only	
	m_subGame->update(SGIDX_CSSERVCARDTWO,hand->getCardAt(0));

	m_subGame->setServerOnly(SGIDX_CSSERVCARDTHREE,true); //server only	
	m_subGame->update(SGIDX_CSSERVCARDTHREE,hand->getCardAt(1));

	m_subGame->setServerOnly(SGIDX_CSSERVCARDFOUR,true); //server only	
	m_subGame->update(SGIDX_CSSERVCARDFOUR,hand->getCardAt(2));

	//Figure out how much their first pick will be worth, and tell the client
	int32 pickValue = getRandomFromArray(g_BigBonusPayouts, 128)*(totalBet);
	m_subGame->update(SGIDX_CSNEXTPICKVAL,pickValue); 		

	//Update the rest of the information
	m_subGame->update(SGIDX_BONUSGAME,BGCARDSHARK);
	m_subGame->update(SGIDX_ISDONE,0);
	m_subGame->update(SGIDX_CSCARDTWO,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSCARDTHREE,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSCARDFOUR,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSCARDFIVE,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSPICKONE,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSPICKTWO,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSVALUETWO,SGNOTPICKED); 
	m_subGame->update(SGIDX_CSPICKTHREE,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSVALUETHREE,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSPICKFOUR,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSVALUEFOUR,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSPICKFIVE,SGNOTPICKED); 	
	m_subGame->update(SGIDX_CSVALUEFIVE,SGNOTPICKED); 	

	//And put in the spacers
	m_subGame->update(SGIDX_CHESTONEY,0);
	m_subGame->update(SGIDX_CHESTTWOY,0);
	m_subGame->update(SGIDX_CHESTTHREEY,0);
	m_subGame->update(SGIDX_CHESTVALUE,0);

}


void
CardSharkSlotGuts::rebuildHand()
{
	CardMgr* cards;
	Shue* shue;
	uint32 deckSeed;
	uint8 cardsInHand = 4;

	//Shuffle the deck
	cards = new CardMgr();

	ShueGameInfo sgi;
	sgi.gameId = GAMEID;
	sgi.machId = getMachId();

	shue = new Shue(cards,1,sgi); //1 deck
	hand = new CardSharkHand(cards);
	deckSeed = m_subGame->getData(SGIDX_CSSERVDECKSEED);
	shue->shuffle(deckSeed);
	//Deal four cards
	for(uint8 i=0; i<cardsInHand; i++)
	{
		hand->add(shue->getCard());
	}


}
void 
CardSharkSlotGuts::playBonusGame(uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData)
{
	bool winner = false;
	bool problem = false;
	int32 whichPick = -1;
	int32 pickValue = -1;
	int32 localChoice = SGNOTPICKED;
	int32 thisCard = SGNOTPICKED;
	int32 nextValue = SGNOTPICKED;

	if(m_subGame->getData(SGIDX_BONUSGAME)!=CardSharkSlotGuts::BGCARDSHARK)
	{
		//Trying to play when you're not in a bonus game!
		problem = true;
		Log::msgWarning("Card Shark - Trying to play bonus game when not in a bonus game: %d", currentGameNum);
	}


	if(bonusGameType!=CardSharkSlotGuts::BGCARDSHARK)
	{
		//We should only get here with pie picks	
		problem = true;
		Log::msgWarning("Card Shark - Trying to play wrong bonus game type: %d", currentGameNum);
	}

	if(bonusDataCount!=1 || ((bonusData[0]!=BGCSHIGH) && (bonusData[0]!=BGCSLOW)))
	{
		//We should only have one pick, and it should be  BGCSHIGH or BGCSLOW	
		problem = true;
		Log::msgWarning("Card Shark - Funny user pick (%d): %d", bonusData[0],currentGameNum);
	}
	else
	{
		localChoice = bonusData[0];
	}


	if(!problem)
	{

		//Restore the game-specific variables
		if((this->getSlotSpin(this->getSlotSpinCount()-1)) != (SlotSpin *) 0)
		{
			currentGameNum = (this->getSlotSpin(this->getSlotSpinCount()-1))->getGameNum();
			totalBet =(this->getSlotSpin(this->getSlotSpinCount()-1))->getTotalBet();
			rebuildHand();


			//Figure out which (sequential) pick this is (first, second, etc.)
			//We don't use loops, so that we can change the order whenever we'd like
			whichPick = -1;
			if(m_subGame->getData(SGIDX_CSPICKONE)==SGNOTPICKED)
			{
				whichPick = 1;
			}
			else if(m_subGame->getData(SGIDX_CSPICKTWO)==SGNOTPICKED)
			{
				whichPick = 2;
			}
			else if(m_subGame->getData(SGIDX_CSPICKTHREE)==SGNOTPICKED)
			{
				whichPick = 3;
			}
			else if(m_subGame->getData(SGIDX_CSPICKFOUR)==SGNOTPICKED)
			{
				//This is only here in case we decide to add anohter card
				whichPick = -1;
			}
			else if(m_subGame->getData(SGIDX_CSPICKFIVE)==SGNOTPICKED)
			{
				//This is only here in case we decide to add anohter card
				whichPick = -1;
			}

			if(whichPick < 0)
			{
				//We've picked way too many
				ASSERT(0);
				Log::msgWarning("Card Shark - Too many picks (whichpick = %d): %d", whichPick,currentGameNum);
			}

			
			//Did they win
			winner = false;
			if((hand->getRankAt(whichPick)>hand->getRankAt(whichPick-1))&&(localChoice == BGCSHIGH))
			{
				winner = true;
			}
			else if ((hand->getRankAt(whichPick)<hand->getRankAt(whichPick-1))&&(localChoice == BGCSLOW))
			{
				winner = true;
			}
			thisCard = hand->getCardAt(whichPick);


			if(winner)
			{
				//pay the player
				pickValue = m_subGame->getData(SGIDX_CSNEXTPICKVAL);
				//Send them the card
				//Is it the last pick?
				if(whichPick>=3)
				{
					m_subGame->update(SGIDX_ISDONE,1);
					m_subGame->update(SGIDX_CSNEXTPICKVAL,0);
					nextValue = 0;
					readytofinish = true;
				}
				else
				{
					//otherwise, set up the next one
					m_subGame->update(SGIDX_CSNEXTPICKVAL,getRandomFromArray(g_BigBonusPayouts, 128)*(totalBet)*(whichPick+1)); //truncation is on purpose
					readytofinish = false;
				}
			}
			else //loser
			{
				//don't pay the player
				pickValue = 0;
				m_subGame->update(SGIDX_CSNEXTPICKVAL,0);
				m_subGame->update(SGIDX_ISDONE,1);
				readytofinish = true;
			}

			//Write out the result
			switch(whichPick)
			{
				case 1:
					m_subGame->update(SGIDX_CSPICKONE,localChoice);
					m_subGame->update(SGIDX_CSVALUEONE,pickValue);
					m_subGame->update(SGIDX_CSCARDTWO,thisCard);
					break;
				case 2:
					m_subGame->update(SGIDX_CSPICKTWO,localChoice);
					m_subGame->update(SGIDX_CSVALUETWO,pickValue);
					m_subGame->update(SGIDX_CSCARDTHREE,thisCard);
					break;
				case 3:
					m_subGame->update(SGIDX_CSPICKTHREE,localChoice);
					m_subGame->update(SGIDX_CSVALUETHREE,pickValue);
					m_subGame->update(SGIDX_CSCARDFOUR,thisCard);
					break;
				case 4:
					//Shouldn't be here, it's only for expansion
					pickValue = 0;
					m_subGame->update(SGIDX_CSPICKFOUR,localChoice);
					m_subGame->update(SGIDX_CSVALUEFOUR,pickValue);
					m_subGame->update(SGIDX_CSCARDFIVE,thisCard);
					break;
				case 5:
					//Shouldn't be here, it's only for expansion
					pickValue = 0;
					Log::msgWarning("Card Shark - Picking a fifth card: %d", currentGameNum);
					m_subGame->update(SGIDX_CSPICKFIVE,localChoice);
					m_subGame->update(SGIDX_CSVALUEFIVE,pickValue);
		//			m_subGame->update(SGIDX_CSCARDSIX,thisCard);
					break;
			}
    
			//Write to the database
			//We write two times -  once with the card, once with the result
			createBonusGame(currentGameNum,10,whichPick*2,BGCARDSHARK,localChoice,0);
			createBonusGame(currentGameNum,10,(whichPick*2)+1,BGCARDSHARK,localChoice,pickValue*(m_info->creditValue));
			delete hand;


			//If we're done, finish the game
			if(isGameComplete())
			{
				completeGame();
			}
		} 
		else //can't load spin
		{
				Log::msgWarning("Card Shark - Can't load spin: %d", whichPick,currentGameNum);
		}
	}
}

bool8	
CardSharkSlotGuts::saveGame()
{
	//Base class
	bool8 result = SlotGuts::saveGame();

	if(result && !readytofinish)
	{
		//get the gamenum
		currentGameNum = (this->getSlotSpin(this->getSlotSpinCount()-1))->getGameNum();
		
		//And write what you can
		//In the icecream case, we can finish things off
		if(m_subGame->getData(SGIDX_BONUSGAME) == BGCHESTS)
		{
			//Ice Cream
			if(createBonusGame(currentGameNum,10,0,BGCHESTS,0,(m_subGame->getData(SGIDX_CHESTVALUE)*(m_info->creditValue))))
			{
				readytofinish = true;
			}
			else
			{
				result = false;
			}
		}
		else
		{
			//Food Fight
			if(!createBonusGame(currentGameNum,10,0,BGCARDSHARK,m_subGame->getData(SGIDX_CSCARDONE),0))
			{
				result = false;
			}

		}

	}

	return result;
}

bool8 
CardSharkSlotGuts::isGameComplete()
{
    return readytofinish;
}


